﻿using OpenCvSharp;
using OpenCvSharp.Extensions;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;

using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZXing;
using ZXing.Common;
using ZXing.Windows.Compatibility;
//install-package OpenCvSharp4.runtime.win
//install-package OpenCvSharp4.Extensions


namespace nswer_sheet
{
    internal class nswersheetHelper
    {
        public static string[] GetFiles()
        {
            string folder = @"D:\Oamist\PIC";
            int idx = 0;
            string[] files = Directory.GetFiles(folder, "*.*")
                                      .Where(f => f.EndsWith(".jpg") || f.EndsWith(".png") || f.EndsWith(".bmp"))
                                      .ToArray();
            return files;
        }
        public static string 识别条形码(System.Drawing.Bitmap filename)
        {   //install-package Zxing.net
            //install-package Zxing.net.Bindings.windows.compatibility
            //创建 ZXing.Net 的 BarcodeReader
            //using ZXing;
            //using ZXing.Common;
            //using ZXing.Windows.Compatibility;
            BarcodeReader reader = new BarcodeReader();
            // 设置字符集和识别选项
            reader.Options = new DecodingOptions
            {
                CharacterSet = "UTF-8",
                // 添加其他优化设置（可选）
                TryHarder = true,   // 更耗时但更准确
                PossibleFormats = new List<BarcodeFormat> {
                    BarcodeFormat.QR_CODE,  // 根据实际类型添加
                    BarcodeFormat.CODE_128,
                    BarcodeFormat.CODE_39,
                    BarcodeFormat.EAN_8,
                    BarcodeFormat.EAN_13          }
            };
            // 执行识别
            Result result = reader.Decode(filename);
            return result?.Text ?? "";
        }
        /// <summary>
        /// 在 Mat 上显示blocks的可视化方法             
        /// </summary>
        /// <param name="background"></param>
        /// <param name="blocks"></param>
        /// <param name="groupTolerance"></param>
        /// <returns></returns>
        public static Mat DrawBlockNumbers(Mat background, List<Rect> blocks, int groupTolerance = 20)
        {
            //Install-Package OpenCvSharp4 -Version 4.8.0
            //Install-Package OpenCvSharp4.runtime.win -Version 4.8.0
            //Install-Package OpenCvSharp4.Extensions
            //using OpenCvSharp;
            //using Point = OpenCvSharp.Point;

            // 1. 准备结果图像
            Mat result = background.Channels() == 1
                ? background.CvtColor(ColorConversionCodes.GRAY2BGR)
                : background.Clone();

            if (blocks == null || !blocks.Any())
            {
                Cv2.PutText(result, "No blocks detected",
                    new Point(10, 30),
                    HersheyFonts.HersheySimplex, 0.8, Scalar.Red, 2);
                return result;
            }

            // 2. 简化颜色方案（预定义颜色数组）
            Scalar[] palette = {
                Scalar.FromRgb(255, 100, 100), // 红
                Scalar.FromRgb(100, 255, 100), // 绿
                Scalar.FromRgb(100, 100, 255), // 蓝
                Scalar.FromRgb(255, 255, 100), // 黄
                Scalar.FromRgb(255, 100, 255), // 粉
                Scalar.FromRgb(100, 255, 255)  // 青
             };

            // 3. 绘制区块
            for (int i = 0; i < blocks.Count; i++)
            {
                Rect block = blocks[i];
                int groupKey = block.Y / groupTolerance;

                // 3.1 取模选择颜色
                Scalar color = palette[groupKey % palette.Length];

                // 3.2 绘制区块背景（半透明）
                using (Mat overlay = result.Clone())
                {
                    Cv2.Rectangle(overlay, block, color, -1);
                    Cv2.AddWeighted(overlay, 0.2, result, 0.8, 0, result);
                }

                // 3.3 绘制边框
                Cv2.Rectangle(result, block, color, 2);

                // 3.4 绘制编号（自适应大小）
                double fontSize = Math.Clamp(block.Height / 30, 0.5, 20);
                Point textPos = new Point(block.X + 5, block.Y + block.Height - 5);

                // 白字黑边保证可读性
                Cv2.PutText(result, $"{i + 1}", textPos + new Point(1, 1),
                    HersheyFonts.HersheySimplex, fontSize, Scalar.Black, 2);
                Cv2.PutText(result, $"{i + 1}", textPos,
                    HersheyFonts.HersheySimplex, fontSize, Scalar.White, 1);
            }

            // 4. 添加统计信息
            int groupCount = blocks.GroupBy(b => b.Y / groupTolerance).Count();
            Cv2.PutText(result, $"Blocks: {blocks.Count} | Groups: {groupCount}",
                new Point(10, 30),
                HersheyFonts.HersheySimplex, 0.7, Scalar.Yellow, 2);

            return result;
        }

        #region 答题涂块大小和间距的计算
        public double Spacing { get; private set; } //答题卡块之间的距离
        public Size BlockSize { get; private set; } //答题涂块的大小

        private Mat _sourceImage;
        public Mat SourceImage
        {
            get => _sourceImage;
            set
            {
                _sourceImage = value?.Clone();
                if (_sourceImage != null)
                {
                    ProcessImage();
                }
            }
        }
        private void ProcessImage()
        {
            // 1. 确保输入图像二值化
            Mat binaryImage = EnsureBinary(_sourceImage);
            Cv2.BitwiseNot(binaryImage, binaryImage);

            // 2. 实现类似Photoshop的最小值操作（腐蚀）
            Mat erodedImage = ApplyMinimumFilter(binaryImage, size: 1);
            erodedImage.SaveImage("R:\\E.JPG");  // 调试用，可移除

            // 3. 找出所有连通区域的外接矩形
            List<Rect> boundingRects = FindAllBoundingRects(erodedImage);

            // 4. 计算并设置属性
            Spacing = CalculateHorizontalSpacingMode(boundingRects);
            BlockSize = CalculateBlockSize(boundingRects);

            // 清理资源
            binaryImage.Dispose();
            erodedImage.Dispose();
        }
        // 确保输入图像二值化
        public Mat EnsureBinary(Mat input)
        {
            // 如果已经是单通道二值图像，直接返回副本
            if (input.Channels() == 1 && input.Type() == MatType.CV_8UC1)
            {
                // 检查是否已经是二值图像
                double minVal, maxVal;
                Cv2.MinMaxLoc(input, out minVal, out maxVal);
                if (minVal == 0 && maxVal == 255)
                    return input.Clone();
            }

            // 转换为灰度图像
            Mat gray = new Mat();
            if (input.Channels() == 3)
            {
                Cv2.CvtColor(input, gray, ColorConversionCodes.BGR2GRAY);
            }
            else if (input.Channels() == 4)
            {
                Cv2.CvtColor(input, gray, ColorConversionCodes.BGRA2GRAY);
            }
            else
            {
                gray = input.Clone();
            }

            // 二值化处理
            Mat binary = new Mat();
            Cv2.Threshold(gray, binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);

            // 清理临时资源
            if (gray != input) gray.Dispose();

            return binary;
        }

        // 实现类似Photoshop的最小值操作（腐蚀）
        public  Mat ApplyMinimumFilter(Mat binaryImage, int size)
        {
            // 创建矩形结构元素
            Mat kernel = Cv2.GetStructuringElement(
                MorphShapes.Rect,
                new Size(2 * size + 1, 2 * size + 1),
                new Point(size, size));

            // 应用腐蚀操作（最小值）
            Mat eroded = new Mat();
            Cv2.Erode(binaryImage, eroded, kernel, iterations: 1);

            kernel.Dispose();
            return eroded;
        }

        // 找出所有连通区域的外接矩形
        private List<Rect> FindAllBoundingRects(Mat binaryImage)
        {
            List<Rect> boundingRects = new List<Rect>();

            // 查找轮廓
            Point[][] contours;
            HierarchyIndex[] hierarchy;
            Cv2.FindContours(
                binaryImage,
                out contours,
                out hierarchy,
                RetrievalModes.List,
                ContourApproximationModes.ApproxSimple);

            // 计算每个轮廓的外接矩形
            foreach (var contour in contours)
            {
                // 过滤太小的轮廓
                double area = Cv2.ContourArea(contour);
                if (area < 10) continue;

                Rect rect = Cv2.BoundingRect(contour);
                boundingRects.Add(rect);
            }

            return boundingRects;
        }
        // 计算水平间距的众数
        private double CalculateHorizontalSpacingMode(List<Rect> rects)
        {
            if (rects.Count < 2) return 0;

            // 按X坐标排序
            var sortedRects = rects.OrderBy(r => r.X).ToList();

            // 计算水平间距
            List<double> spacings = new List<double>();
            for (int i = 1; i < sortedRects.Count; i++)
            {
                Rect prev = sortedRects[i - 1];
                Rect current = sortedRects[i];

                // 计算间距：当前矩形左边界 - 前一个矩形右边界
                double spacing = current.X - (prev.X + prev.Width);


                if (spacing > 0) // 只考虑正间距
                {
                    spacings.Add(spacing);
                }
            }

            if (spacings.Count == 0) return 0;

            // 计算众数（使用四舍五入到整数简化统计）
            Dictionary<int, int> frequencyMap = new Dictionary<int, int>();
            foreach (double spacing in spacings)
            {
                int rounded = (int)Math.Round(spacing);
                if (frequencyMap.ContainsKey(rounded))
                {
                    frequencyMap[rounded]++;
                }
                else
                {
                    frequencyMap[rounded] = 1;
                }
            }

            // 找到最高频率的间距值
            var mode = frequencyMap
                .OrderByDescending(kv => kv.Value)
                .First();

            return mode.Key;
        }
        // 新增方法：计算填涂块的代表性尺寸
        private Size CalculateBlockSize(List<Rect> rects)
        {
            if (rects == null || rects.Count == 0)
                return new Size(0, 0);

            // 分别计算宽度和高度的众数
            int widthMode = CalculateMode(rects.Select(r => r.Width));
            int heightMode = CalculateMode(rects.Select(r => r.Height));

            return new Size(widthMode, heightMode);
        }

        // 新增辅助方法：计算整数序列的众数
        private int CalculateMode(IEnumerable<int> values)
        {
            var groups = values.GroupBy(v => v)
                              .OrderByDescending(g => g.Count())
                              .ToList();

            return groups.Count > 0 ? groups.First().Key : 0;
        }

        public static (double averageSpacing, Size averageBlockSize) CalculateAverages(
    List<double> spacings,
    List<Size> blockSizes)
        {
            // 验证输入数据
            if (spacings == null || blockSizes == null)
                throw new ArgumentNullException("Input lists cannot be null.");
            if (spacings.Count != blockSizes.Count)
                throw new ArgumentException("Both lists must have the same count.");
            if (spacings.Count == 0)
                return (0, new Size());

            // 1. 检测并过滤spacings中的异常值
            List<int> validIndices = GetValidIndices(spacings);

            // 2. 计算有效数据的平均值
            return CalculateValidAverages(spacings, blockSizes, validIndices);
        }

        private static List<int> GetValidIndices(List<double> spacings)
        {
            // 计算中位数
            var sorted = new List<double>(spacings);
            sorted.Sort();
            double median = sorted[sorted.Count / 2];
            if (sorted.Count % 2 == 0)
                median = (sorted[sorted.Count / 2 - 1] + sorted[sorted.Count / 2]) / 2.0;

            // 计算绝对偏差和中位数绝对偏差(MAD)
            List<double> deviations = new List<double>();
            foreach (double val in spacings)
                deviations.Add(Math.Abs(val - median));

            deviations.Sort();
            double mad = deviations[deviations.Count / 2];
            if (deviations.Count % 2 == 0)
                mad = (deviations[deviations.Count / 2 - 1] + deviations[deviations.Count / 2]) / 2.0;

            // 确定有效数据范围（MAD为0时表示所有数据相同）
            double lowerBound = median - 3 * 1.4826 * mad;
            double upperBound = median + 3 * 1.4826 * mad;

            // 收集有效数据的索引
            List<int> validIndices = new List<int>();
            for (int i = 0; i < spacings.Count; i++)
            {
                if (mad == 0 || (spacings[i] >= lowerBound && spacings[i] <= upperBound))
                    validIndices.Add(i);
            }

            return validIndices;
        }

         static (double avgSpacing, Size avgSize) CalculateValidAverages(
            List<double> spacings,
            List<Size> blockSizes,
            List<int> validIndices)
        {
            // 计算平均间距
            double spacingSum = 0;
            foreach (int idx in validIndices)
                spacingSum += spacings[idx];
            double avgSpacing = spacingSum / validIndices.Count;

            // 计算平均区块尺寸
            double widthSum = 0;
            double heightSum = 0;
            foreach (int idx in validIndices)
            {
                widthSum += blockSizes[idx].Width;
                heightSum += blockSizes[idx].Height;
            }
            Size avgSize = new Size(
                (int)Math.Round(widthSum / validIndices.Count),
                (int)Math.Round(heightSum / validIndices.Count)
            );

            return (avgSpacing, avgSize);
        }

        #endregion

    }
}
